//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<CGeometryLoader.cpp>
///	@path	~/src/scene/loader/
///	@date	2008/04/16
///	@desc	.

#include "config/config.h"

#include <boost/tuple/tuple.hpp>

#include "lib/utilities/helper.h"

#include "database/store/data_loader.h"

#include "scene/loader/CGeometryLoader.h"

namespace xeres {

	IMPL_IOBJECT_CLASS( CSimpleGeometryLoader );

	// CSimpleGeometryLoader
	CSimpleGeometryLoader::CSimpleGeometryLoader( void )
	{
		TRACE_INFO( _S("Created simple geometry loader...") );
	}
	// ~CSimpleGeometryLoader
	CSimpleGeometryLoader::~CSimpleGeometryLoader( void )
	{
	}

	// Simple geometry declaration parsing
	namespace
	{
		class _SimpleGeometryDecl
		{
		public:

			struct _TypeInfo
			{
				VertexSlotType slot_type;
				size_t type_size;
				ClassID type;
				const char * semantic;
				bool color_order;

				/// \ctor
				_TypeInfo( void )
					: slot_type( VertexSlotType_last ) , type_size( 0 ) , type( INVALID_CLASS_ID ) , semantic( "" ) , color_order( false )
				{

				}

				/// \ctor
				_TypeInfo( VertexSlotType slot , size_t size , ClassID type , const char * semantic , bool order )
					: slot_type( slot ) , type_size( size ) , type( type ) , semantic( semantic ) , color_order( order )
				{
				}

				/// \ctor
				_TypeInfo( const _TypeInfo& rhs )
					: slot_type( rhs.slot_type ) , type_size( rhs.type_size ) , type( rhs.type )
					, semantic( rhs.semantic ) , color_order( rhs.color_order )
				{
				}
			};

			/// \instance
			static _SimpleGeometryDecl& instance( void )
			{
				static _SimpleGeometryDecl s_SimpleGeometryDecl_instance;
				return s_SimpleGeometryDecl_instance;
			}

			/// \brief	Query decl.
			_TypeInfo QueryDecl( const WString& name )
			{
				dict< WString, _TypeInfo >::iterator it = m_declmap.find( name );
				if( it == m_declmap.end() )
				{
					return _TypeInfo();
				}
				return it->second;
			}

		private:
			
			/// \ctor
			_SimpleGeometryDecl( void )
			{
				m_declmap[_S("position")] = _TypeInfo( VertexSlotType_Float3 , sizeof(float)*3 , VECTOR_TYPE_CLASS_ID , "POSITION" , false );
				m_declmap[_S("positionT")] = _TypeInfo( VertexSlotType_Float4 , sizeof(float)*4 , QUAT_TYPE_CLASS_ID , "POSITIONT" , false );
				m_declmap[_S("normal")] = _TypeInfo( VertexSlotType_Float3 , sizeof(float)*3 , VECTOR_TYPE_CLASS_ID , "NORMAL" , false );
				m_declmap[_S("tangent")] = _TypeInfo( VertexSlotType_Float3 , sizeof(float)*3 , VECTOR_TYPE_CLASS_ID , "TANGENT" , false );
				m_declmap[_S("diffuse")] = _TypeInfo( VertexSlotType_ColorRGBA , sizeof(byte)*4 , INT_TYPE_CLASS_ID , "COLOR" , true );
				m_declmap[_S("specular")] = _TypeInfo( VertexSlotType_ColorRGBA , sizeof(byte)*4 , INT_TYPE_CLASS_ID , "COLOR1" ,  true );
				m_declmap[_S("psize")] = _TypeInfo( VertexSlotType_Float1 , sizeof(float) , FLOAT_TYPE_CLASS_ID , "PSIZE" , false );
				m_declmap[_S("tex0")] = _TypeInfo( VertexSlotType_Float2 , sizeof(float)*2 , PAIR_TYPE_CLASS_ID , "TEXCOORD0" , false );
				m_declmap[_S("tex1")] = _TypeInfo( VertexSlotType_Float2 , sizeof(float)*2 , PAIR_TYPE_CLASS_ID , "TEXCOORD1" , false );
				m_declmap[_S("tex2")] = _TypeInfo( VertexSlotType_Float2 , sizeof(float)*2 , PAIR_TYPE_CLASS_ID , "TEXCOORD2" , false );
				m_declmap[_S("tex3")] = _TypeInfo( VertexSlotType_Float2 , sizeof(float)*2 , PAIR_TYPE_CLASS_ID , "TEXCOORD3" , false );
			}

			// @ data
			dict< WString, _TypeInfo >	m_declmap;
		};
	}

	// OnLoad
	RefPass<IGeometry> CSimpleGeometryLoader::OnLoad( RefWeak<IData> res , ResourceLoader& loader )
	{
		TRACK_FUNCTION_SCOPE();

		// load simple geometry
		
		// get geometry type
		WString typeName;
		if( !res->GetAttribute( _S("type") , typeName ) )
		{
			TRACE_ERROR( _S("CSimpleGeometryLoader::OnLoad: Topology type is not specified.") );
			return NULL;
		}

		TopologyType type = IGeometry::WhichTopologyType( typeName );
		if( type == TOPOLOGY_last )
		{
			TRACE_ERROR( _S("CSimpleGeometryLoader::OnLoad: Topology type '%s' is invalid.") , typeName.c_str() );
			return NULL;
		}

		// load vertex
		Ref<IData> vert = res->GetChild( WString(_S("vert")) );

		// get vertex count
		WString countDesc;
		if( !vert->GetAttribute( _S("count") , countDesc ) )
		{
			TRACE_ERROR( _S("CSimpleGeometryLoader::OnLoad: Vertex count is not defined.") );
			return NULL;
		}

		int vertCount = 0;
		if( swscanf( countDesc.c_str() , _S("%i") , &vertCount ) != 1 || vertCount <= 0 )
		{
			TRACE_ERROR( _S("CSimpleGeometryLoader::OnLoad: Vertex count declaration is invalid.") );
			return NULL;
		}

		// handle color order, find rgba or argb
		WString colorOrder;
		vert->GetAttribute( _S("colororder") , colorOrder );

		ColorOrder corder = COLOR_ORDER_last;
		ColorOrder rorder = loader.QueryColorOrder();

		//  parse declaration
		std::vector<VertexSlotDeclaration> decls;
		std::vector<ClassID> structure;
		std::vector< std::pair<VertexSlotType,size_t> > orderChange;

		size_t offset = 0;

		WString decl;
		if( !vert->GetAttribute( _S("decl") , decl ) )
		{
			TRACE_ERROR( _S("CSimpleGeometryLoader::OnLoad: Vertex declaration is not defined.") );
			return NULL;
		}

		StringSeq seq;
		break_string( decl , '|' , seq );
		for( size_t i = 0 , total = seq.size() ; i < total ; ++i )
		{
			_SimpleGeometryDecl::_TypeInfo info = _SimpleGeometryDecl::instance().QueryDecl( seq[i] );
			if( info.slot_type == VertexSlotType_last )
			{
				TRACE_ERROR( _S("CSimpleGeometryLoader::OnLoad: Vertex declaration '%s' is invalid.") , seq[i].c_str() );
				return NULL;
			}

			// color order
			if( info.color_order )
			{
				if( corder == COLOR_ORDER_last)
				{
					if( colorOrder == _S("argb") || colorOrder == _S("ARGB") )
					{
						corder = COLOR_ORDER_ARGB;
					}
					else if( colorOrder == _S("rgba") || colorOrder == _S("RGBA") )
					{
						corder = COLOR_ORDER_RGBA;
					}
					else
					{
						TRACE_ERROR( _S("CSimpleGeometryLoader::OnLoad: Unknown color order type [%s].") , colorOrder.c_str() );
						return NULL;
					}
				}
				if( corder != rorder )
				{
					orderChange.push_back( std::make_pair( info.slot_type , offset ) );
				}
			}
			decls.push_back( VertexSlotDeclaration( offset , 0 , info.slot_type , info.semantic ) );
			offset += info.type_size;
			structure.push_back( info.type );
		}
		Ref<IVertexLayout> layout = loader.Renderer()->DeclareVertexLayout( decls );
		seq.clear();

		// create buffer
		size_t stride = offset;
		byte * buffer = (byte*)malloc( stride * vertCount );
		Guard<_free> _bufferGuard( buffer );

		WString cdata;
		vert->GetString( cdata );

		if( !DataLoader::ParseCData( structure , vertCount , cdata , buffer , stride ) )
		{
			TRACE_ERROR( _S("CSimpleGeometryLoader::OnLoad: Data corrupted.") );
			return NULL;
		}

		// change order of color
		if( !orderChange.empty() )
		{
			byte * p = buffer;
			for( int i = 0 ; i < vertCount ; ++i , p += stride )
			{
				for( size_t c = 0 ; c < orderChange.size() ; ++c )
				{
					VertexSlotType id = orderChange[c].first;
					size_t offset = orderChange[c].second;
					if( id == VertexSlotType_ColorRGBA )
					{
						int * cl = (int*)(p + offset);
						*cl = d3d_convert_color( *cl );
					}
					else
					{
						TRACE_ERROR( _S("CSimpleGeometryLoader::OnLoad: Invalid color type.") );
						return NULL;
					}
				}
			}
		}
		return loader.Renderer()->CreateSimplePolygon( type , layout , vertCount , buffer , 0 , NULL );
	}

	IMPL_IOBJECT_CLASS( CGeometryLoader );

	// CGeometryLoader
	CGeometryLoader::CGeometryLoader( void )
	{
		TRACE_INFO( _S("Created general geometry loader...") );
	}
	// ~CGeometryLoader
	CGeometryLoader::~CGeometryLoader( void )
	{
	}
	// OnLoad
	RefPass<IGeometry> CGeometryLoader::OnLoad( RefWeak<IData> res , ResourceLoader& loader )
	{
		return NULL;
	}

} // namespace xeres
